﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using DTGroup.Networking.Framework;
using DTGroup.Networking.Framework.Utils;

namespace Utility
{
    /// <summary>
    /// (线程安全(同一款软件中)的)全局文件锁，用来配合并行写入文件.
    /// </summary>
    public sealed class FileLockerManager
    {
        private static object syncLocker = new object();
        private static IList<string> lockedFiles = new List<string>();

        public static void Locked(string filePath)
        {
            lock (syncLocker)
            {
                if (!lockedFiles.Contains(filePath))
                {
                    lockedFiles.Add(filePath);
                }
            }
        }

        public static void UnLock(string filePath)
        {
            lock (syncLocker)
            {
                if (lockedFiles.Contains(filePath))
                {
                    lockedFiles.Remove(filePath);
                }
            }
        }

        public static bool HasLocked(string filePath)
        {
            lock (syncLocker)
            {
                return lockedFiles.Contains(filePath);
            }
        }
    }

    public partial class FileUtil
    {
        private static NLog.Logger logger = new NLog.LogFactory().GetCurrentClassLogger();

        /// <summary>
        /// 返回文件大小（单位：字节）
        /// </summary>
        /// <param name="dir"></param>
        /// <returns></returns>
        public static long GetDiretcoryInnerFileSize(string dir)
        {
            long size = 0;

            DirectoryInfo directoryInfo = new DirectoryInfo(dir);

            if (!directoryInfo.Exists)
                return size;

            FileInfo[] files = directoryInfo.GetFiles();
            files.ForEach((FileInfo m) =>
            {
                size += m.Length;
            });

            return size;
        }

        /// <summary>
        /// 线程安全的并行向文件中写入内容
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="content"></param>
        /// <param name="timeout">过期时间（默认60s）</param>
        public static void SafeParallelWriteToFile(string filePath, string content, bool wirterNewLine = true, int timeout = 60*1000)
        {
            int waitTime = 0;

        ReTrySeekStatus:
            if (FileLockerManager.HasLocked(filePath))
            {
                if (waitTime <= timeout)
                {
                    Thread.Sleep(200);
                    waitTime += 200;
                }
                else
                {
                    throw new TimeoutException("等待写入时间过期。。。。");
                }

                goto ReTrySeekStatus;
            }

            try
            {
                FileLockerManager.Locked(filePath);

                FileStream fs = null;
                if (!File.Exists(filePath))
                {
                    fs = File.Create(filePath);
                }
                else
                {
                    fs = File.Open(filePath, FileMode.Append, FileAccess.Write, FileShare.Write);
                }

                using (StreamWriter sw = new StreamWriter(fs))
                {
                    if (wirterNewLine)
                    {
                        sw.WriteLine(content);
                    }
                    else
                    {
                        sw.Write(content);
                    }
                    sw.Flush();
                }
                fs.Close();
            }
            catch (Exception ex)
            {
                logger.Error("并行写入文件失败：\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
            }
            finally
            {
                FileLockerManager.UnLock(filePath);
            }
        }

        /// <summary>
        /// 大文件分隔为多个小文件
        /// </summary>
        /// <param name="file">大文件路径</param>
        /// <param name="splitMinFileSize">被分割的大文件最小分割大小</param>
        /// <param name="splitFileSavePathFormat">小文件存储路径规则</param>
        /// <param name="fileType">小文件类型：S1_U|S1_MME</param>
        /// <param name="splitFileSize">分隔出来的每个小文件的大小</param>
        /// <returns></returns>
        public static List<string> Split(System.ComponentModel.BackgroundWorker backgroundWorker, string file, long splitMinFileSize, string splitFileSavePathFormat, string fileType, int splitFileSize)
        {
            List<string> filePaths = new List<string>();

            FileInfo fileInfo = new FileInfo(file);

            // 不需要分隔文件！
            if (fileInfo.Length <= splitMinFileSize)
            {
                filePaths.Add(file);
                return filePaths;
            }

            string fileDir = fileInfo.Directory.FullName;
            string fileName = fileInfo.Name;
            string fileExtension = fileInfo.Extension;
            string fileNameWithNoExtension = fileName.Replace(fileExtension, string.Empty);
            // 如果是共享目录，则需要特殊处理分割文件存储规则。
            if (file.IndexOf(":") == -1)
            {
                splitFileSavePathFormat = splitFileSavePathFormat.Replace("\\", "/");
            }

            backgroundWorker.ReportProgress(1, "开始删除该目录“" + fileDir + "”下的所有子目录。。。");
            // 删除该目录下的所有子目录。
            foreach (string dir in Directory.GetDirectories(fileDir))
            {
                Directory.Delete(dir);
            }
            backgroundWorker.ReportProgress(1, "完成删除该目录“" + fileDir + "”下的所有子目录。。。");

            int steps = (int)(fileInfo.Length / splitFileSize);

            backgroundWorker.ReportProgress(1, "开始分割大文件“" + file + "”,预计将分割出“" + steps + "”个子文件。。。");

            using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader br = new BinaryReader(fs))
                {
                    int couter = 1;
                    bool isReadingComplete = false;
                    while (!isReadingComplete)
                    {
                        DateTime begin = DateTime.Now;
                        string filePath = string.Format(splitFileSavePathFormat, fileDir, fileType, string.Concat(fileNameWithNoExtension, "_", couter.ToString("00000000000000"), fileExtension));

                        FileInfo fileInf = new FileInfo(filePath);
                        if (false == Directory.Exists(fileInf.Directory.FullName))
                        {
                            Directory.CreateDirectory(fileInf.Directory.FullName);
                        }
                        logger.Debug("开始读取文件【{1}】：{0}", filePath, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

                        byte[] input = br.ReadBytes(splitFileSize);
                        using (FileStream writeFs = new FileStream(filePath, FileMode.Create))
                        {
                            using (BinaryWriter bw = new BinaryWriter(writeFs))
                            {
                                bw.Write(input);
                            }
                        }

                        isReadingComplete = (input.Length != splitFileSize);
                        if (!isReadingComplete)
                        {
                            couter += 1;
                        }

                        logger.Debug("完成读取文件【{1}】：{0}", filePath, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

                        backgroundWorker.ReportProgress(1, "当前时第" + couter + "个文件,一共个" + steps + "文件。耗时【" + begin.ToString("yyyy-MM-dd HH:mm:ss") + "，" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "】分割子文件文件“" + filePath + "”结束。。。");

                        filePaths.Add(filePath);
                    }
                }
            }

            return filePaths;
        }
    }

    public partial class FileUtil
    {
        private static object syncStaticObject = new object();
        private static FileUtil instance = null;
        private static FileUtil Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncStaticObject)
                    {
                        if (instance == null)
                        {
                            instance = new FileUtil();
                        }
                    }

                }
                return instance;
            }
        }

        public static void AppendOrCreate(string filePath, string content)
        {
            Instance.WriteToFile(filePath, content);
        }

        private object lockObject = new object();
        private object SyncRoot
        {
            get
            {
                return lockObject;
            }
        }

        private void WriteToFile(string filePath, string content)
        {
            lock (SyncRoot)
            {
                FileStream fs = null;
                if (!File.Exists(filePath))
                {
                    fs = File.Create(filePath);
                }
                else
                {
                    fs = File.Open(filePath, FileMode.Append, FileAccess.Write, FileShare.Write);
                }

                StreamWriter sw = new StreamWriter(fs);
                sw.WriteLine(content);
                sw.Flush();
                sw.Close();
                fs.Close();
            }
        }


        /// <summary>
        /// 读取 文件每一行内容
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="readLineContent">读取每行的内容</param>
        public static void ReadFileLineContent(string filePath, Action<string> readLineContent)
        {
            if (string.IsNullOrEmpty(filePath) == false)
            {
                if (System.IO.File.Exists(filePath))
                {
                    if (readLineContent != null)
                    {
                        Stream fileStream = new FileStream(filePath, FileMode.Open);
                        
                        using (StreamReader textReader = new StreamReader(fileStream))
                        {
                            // 文件流 读取的行数
                            string textReadLine = string.Empty;

                            while (textReadLine != null)
                            {
                                textReadLine = textReader.ReadLine();
                                if (textReadLine != null)
                                {
                                    readLineContent(textReadLine);
                                }
                            }
                        }

                        fileStream.Close();
                        fileStream.Dispose();
                    }
                }
            }
        }

        /// <summary>
        ///  获取 文件的编码格式
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <returns>编码格式</returns>
        public static Encoding GetFileEncodeType(string filePath)
        {
            Encoding targetEncoding = Encoding.Default;
            using (FileStream fs = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
            {
                BinaryReader br = new BinaryReader(fs);
                Byte[] buffer = br.ReadBytes(3);

                if (buffer[0] >= 0xEF)
                {
                    //Unicode {0xFF, 0xFE};
                    //BE-Unicode {0xFE, 0xFF};
                    //UTF8 = {0xEF, 0xBB, 0xBF};
                    if (buffer[0] == 0xFE && buffer[1] == 0xFF)//UnicodeBe
                    {
                        targetEncoding = Encoding.BigEndianUnicode;
                    }
                    else if (buffer[0] == 0xFF && buffer[1] == 0xFE && buffer[3] != 0xFF)//Unicode
                    {
                        targetEncoding = Encoding.Unicode;
                    }
                    else if (buffer[1] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF)//UTF8
                    {
                        targetEncoding = Encoding.UTF8;
                    }
                }
            }

            return targetEncoding;
        }

        public static void CreateExportFileAndClearOldFile(string fileRootDir)
        {
            //string fileRootDir = MapPath(exportFile);
            string currentDayFolderName = DateTime.Now.ToString("yyyy-MM-dd");
            string currentDayFileSaveDir = IOUtil.JoinPath(fileRootDir, currentDayFolderName);

            // 清理历史记录
            DirectoryInfo rootDirectoryInfo = new DirectoryInfo(fileRootDir);
            foreach (DirectoryInfo yyyyMMddDir in rootDirectoryInfo.GetDirectories())
            {
                if (yyyyMMddDir.Name == currentDayFolderName)
                {
                    yyyyMMddDir.Delete(true);
                }
            }

            // 创建当天临时文件保存目录
            if (false == IOUtil.ExistsDirectory(currentDayFileSaveDir))
            {
                IOUtil.CreateDirectory(currentDayFileSaveDir);
            }

        }
    }
}
